"use strict";

exports.__esModule = true;
require("core-js/modules/es.error.cause.js");
require("core-js/modules/esnext.iterator.constructor.js");
require("core-js/modules/esnext.iterator.map.js");
var _array = require("../../../helpers/array");
var _mixed = require("../../../helpers/mixed");
var _base = _interopRequireDefault(require("./_base"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
const CHAR_CARRIAGE_RETURN = String.fromCharCode(13);
const CHAR_DOUBLE_QUOTES = String.fromCharCode(34);
const CHAR_LINE_FEED = String.fromCharCode(10);
const CHAR_EQUAL = String.fromCharCode(61);
const CHAR_PLUS = String.fromCharCode(43);
const CHAR_MINUS = String.fromCharCode(45);
const CHAR_AT = String.fromCharCode(64);
const CHAR_TAB = String.fromCharCode(9);

/**
 * @private
 */
var _Csv_brand = /*#__PURE__*/new WeakSet();
class Csv extends _base.default {
  constructor() {
    super(...arguments);
    /**
     * Sanitize value that may be interpreted as a formula in spreadsheet software.
     * Following the OWASP recommendations: https://owasp.org/www-community/attacks/CSV_Injection.
     *
     * @param {string} value Cell value.
     * @returns {string}
     */
    _classPrivateMethodInitSpec(this, _Csv_brand);
  }
  /**
   * Default options for exporting CSV format.
   *
   * @returns {object}
   */
  static get DEFAULT_OPTIONS() {
    return {
      mimeType: 'text/csv',
      fileExtension: 'csv',
      bom: true,
      columnDelimiter: ',',
      rowDelimiter: '\r\n',
      sanitizeValues: false
    };
  }

  /**
   * Create string body in desired format.
   *
   * @returns {string}
   */
  export() {
    const options = this.options;
    const data = this.dataProvider.getData();
    let columnHeaders = this.dataProvider.getColumnHeaders();
    const hasColumnHeaders = columnHeaders.length > 0;
    const rowHeaders = this.dataProvider.getRowHeaders();
    const hasRowHeaders = rowHeaders.length > 0;
    let result = options.bom ? String.fromCharCode(0xFEFF) : '';
    if (hasColumnHeaders) {
      columnHeaders = (0, _array.arrayMap)(columnHeaders, value => this._escapeCell(value, {
        force: true,
        sanitizeValue: options.sanitizeValues
      }));
      if (hasRowHeaders) {
        result += options.columnDelimiter;
      }
      result += columnHeaders.join(options.columnDelimiter);
      result += options.rowDelimiter;
    }
    (0, _array.arrayEach)(data, (value, index) => {
      if (index > 0) {
        result += options.rowDelimiter;
      }
      if (hasRowHeaders) {
        result += this._escapeCell(rowHeaders[index], {
          sanitizeValue: options.sanitizeValues
        });
        result += options.columnDelimiter;
      }
      const escapedValue = value.map(cellValue => this._escapeCell(cellValue, {
        sanitizeValue: options.sanitizeValues
      })).join(options.columnDelimiter);
      result += escapedValue;
    });
    return result;
  }

  /**
   * Escape cell value.
   *
   * @param {*} value Cell value.
   * @param {object} options Options.
   * @param {boolean} [options.force=false] Indicates if cell value will be escaped forcefully.
   * @param {boolean|RegExp|Function} [options.sanitizeValue=false] Controls the sanitization of cell value.
   * @returns {string}
   */
  _escapeCell(value) {
    let {
      force = false,
      sanitizeValue = false
    } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let returnValue = (0, _mixed.stringify)(value);
    if (returnValue === '') {
      return returnValue;
    }
    if (sanitizeValue) {
      force = true;
    }
    if (sanitizeValue instanceof RegExp) {
      returnValue = _assertClassBrand(_Csv_brand, this, _sanitizeValueWithRegExp).call(this, returnValue, sanitizeValue);
    } else if (typeof sanitizeValue === 'function') {
      returnValue = sanitizeValue(returnValue);
    } else if (sanitizeValue) {
      returnValue = _assertClassBrand(_Csv_brand, this, _sanitizeValueWithOWASP).call(this, returnValue);
    }
    if (force || returnValue.indexOf(CHAR_CARRIAGE_RETURN) >= 0 || returnValue.indexOf(CHAR_DOUBLE_QUOTES) >= 0 || returnValue.indexOf(CHAR_LINE_FEED) >= 0 || returnValue.indexOf(this.options.columnDelimiter) >= 0) {
      returnValue = returnValue.replace(new RegExp('"', 'g'), '""');
      returnValue = `"${returnValue}"`;
    }
    return returnValue;
  }
}
function _sanitizeValueWithOWASP(value) {
  if (value.startsWith(CHAR_EQUAL) || value.startsWith(CHAR_PLUS) || value.startsWith(CHAR_MINUS) || value.startsWith(CHAR_AT) || value.startsWith(CHAR_TAB) || value.startsWith(CHAR_CARRIAGE_RETURN)) {
    return `'${value}`;
  }
  return value;
}
/**
 * Sanitize value using regular expression.
 *
 * @param {string} value Cell value.
 * @param {RegExp} regexp Regular expression to test against.
 * @returns {string}
 */
function _sanitizeValueWithRegExp(value, regexp) {
  return regexp.test(value) ? `'${value}` : value;
}
var _default = exports.default = Csv;